#include "Rules.h"
#include "Node.h"
#include <assert.h>
#include "Helper.h"
#include <stdio.h>
#include "TokenPrinter.h"
#include "Leave.h"
#include "TypeChecker.h"
#include <stdlib.h>
#include "CodeMaker.h"

const int DEBUG_PRINT_TOKEN_FOUND     = 0;
const int DEBUG_PRINT_TRACE_FUNCTIONS = 0;

Rules::Rules() :
    scanner(NULL),
    t(NULL),
    lastToken(false)
{
    TypeChecker::setRules(this);
}

Rules::~Rules(){}
    
void Rules::setScanner(ScannerV0* scanner)
{
    this->scanner = scanner;
    Leave::setScanner(scanner);
}

void Rules::next_token()
{
    //printf("next_token(), delivers:");
    if(!lastToken){//t is NULL, if the last token is already read
        t = scanner->nextToken();
        if(t == NULL) {
            //printf("NO MORE TOKEN!!!\n");
            lastToken = true;
        }// else { TokenPrinter::printToken(stdout, t, scanner); }
    } else {
        printf("NO MORE TOKEN!!!\n");
    }
}


TType Rules::token_type()
{
    if(t == NULL)//t is NULL, if the last token is already read
        return TOK_NO_TOK;

    TType type = t->getType();
    return type;
    //return t->getType();
}

void Rules::error(const char * const errorDescription, Token* token /* = NULL*/) {
   if(token == NULL)
        token = t;//use member variable instead
   fprintf(stderr, "ERROR at %d:%d, %s\n", token->getRow(), token->getCol(), errorDescription);
   //if(token != NULL) TokenPrinter::printToken(stderr, token, scanner);
   //assert(false);//debugging
   exit(1);
}

ParseTree* Rules::PROG(){
    if(DEBUG_PRINT_TRACE_FUNCTIONS) printf("PROG\n");

    ParseTree* tree = new ParseTree();
    tree->create(TypeChecker::checkPROG, CodeMaker::makePROG);

    next_token();

    Node* DECLS_Node = DECLS();
    tree->add_node(DECLS_Node);

    Node* STATEMENTS_Node = STATEMENTS();
    tree->add_node(STATEMENTS_Node);

    if(t != NULL) { error("invalid token"); }

    return tree;
}

Node* Rules::DECLS(){
    if(DEBUG_PRINT_TRACE_FUNCTIONS) printf("DECLS\n");

    Node* node = new Node();

    if(token_type() == TOK_INT){
        Node* DECL_Node = DECL();
        node->add_node(DECL_Node);

        if(token_type() == TOK_SEMICOLON) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_SEMICOLON"); }

        Node* DECLS_Node = DECLS();
        node->add_node(DECLS_Node);
        node->create(TypeChecker::checkDECLS, CodeMaker::makeDECLS);//sets the typecheck method
   } else {
        node->create(TypeChecker::checkDECLSe, CodeMaker::makeDECLSe);//sets the typecheck method
     //epsilon
   }
    
    return node;
}

Node* Rules::DECL(){
    if(DEBUG_PRINT_TRACE_FUNCTIONS) printf("DECL\n");
    Node* node = new Node();

    if(token_type() == TOK_INT){
        if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

        Leave* leave = new Leave(t);
        node->add_node(leave);

        next_token();

        Node* ARRAY_Node = ARRAY();
        node->add_node(ARRAY_Node);
   
        if(token_type() == TOK_IDENTIFIER) { 
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave =  new Leave(t);
            node->add_node(leave);

            next_token();

        } else { error("expected TOK_IDENTIFIER"); }
        node->create(TypeChecker::checkDECL, CodeMaker::makeDECL);//sets the typecheck method
    }

    return node;
}

Node* Rules::ARRAY(){
    if(DEBUG_PRINT_TRACE_FUNCTIONS) printf("ARRAY\n");
    Node* node = new Node();

    if(token_type() == TOK_SQ_BR_OPEN){
        if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

        Leave* leave = new Leave(t);
        node->add_node(leave);

        next_token();

        if(token_type() == TOK_INTEGER) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();

        } else { error("expected TOK_INTEGER"); }

        if(token_type() == TOK_SQ_BR_CLOSE) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();

        } else { error("expected TOK_INTEGER"); }
        node->create(TypeChecker::checkARRAY, CodeMaker::makeARRAY);//sets the typecheck method
    } else {
        //epsilon
        node->create(TypeChecker::checkARRAYe, CodeMaker::makeARRAYe);//sets the typecheck method
    }

    return node;
}

Node* Rules::STATEMENTS(){
    Node* node = new Node();

    if(token_type() == TOK_IDENTIFIER    || 
       token_type() == TOK_PRINT         ||
       token_type() == TOK_READ          ||
       token_type() == TOK_CURLY_BR_OPEN ||
       token_type() == TOK_IF            ||
       token_type() == TOK_WHILE){
        
        Node* STATEMENT_Node = STATEMENT();
        node->add_node(STATEMENT_Node);

        if(token_type() == TOK_SEMICOLON) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();

        } else { error("expected TOK_SEMICOLON"); }

        Node* STATEMENTS_Node = STATEMENTS();
        node->add_node(STATEMENTS_Node);
        node->create(TypeChecker::checkSTATEMENTS, CodeMaker::makeSTATEMENTS);//sets the typecheck method
    } else {
        //eplison
        node->create(TypeChecker::checkSTATEMENTSe, CodeMaker::makeSTATEMENTSe);//sets the typecheck method
    }

    return node;
}

Node* Rules::STATEMENT(){
    if(DEBUG_PRINT_TRACE_FUNCTIONS) printf("STATEMENT\n");
    Node* node = new Node();

    if(token_type() == TOK_IDENTIFIER){
        if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

        Leave* leave = new Leave(t);
        node->add_node(leave);

        next_token();
        
        Node* INDEX_Node = INDEX();
        node->add_node(INDEX_Node);

        if(token_type() == TOK_ASSIGN) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_ASSIGN"); }

        Node* EXP_Node = EXP();
        node->add_node(EXP_Node);

        node->create(TypeChecker::checkSTATEMENT_ASSIGN, CodeMaker::makeSTATEMENT_ASSIGN);//sets the typecheck method
    } else if(token_type() == TOK_PRINT){
        if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

        Leave* leave = new Leave(t);
        node->add_node(leave);

        next_token();

        if(token_type() == TOK_BR_OPEN) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_BR_OPEN"); }

        Node* EXP_Node = EXP();
        node->add_node(EXP_Node);

        if(token_type() == TOK_BR_CLOSE) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_BR_CLOSE"); }

        node->create(TypeChecker::checkSTATEMENT_PRINT, CodeMaker::makeSTATEMENT_PRINT);//sets the typecheck method
    } else if(token_type() == TOK_READ){
        if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

        Leave* leave = new Leave(t);
        node->add_node(leave);

        next_token();

        if(token_type() == TOK_BR_OPEN) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_BR_OPEN"); }

        if(token_type() == TOK_IDENTIFIER) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_IDENTIFIER"); }

        Node* INDEX_Node = INDEX();
        node->add_node(INDEX_Node);

        if(token_type() == TOK_BR_CLOSE) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_BR_CLOSE"); }
 
        node->create(TypeChecker::checkSTATEMENT_READ, CodeMaker::makeSTATEMENT_READ);//sets the typecheck method
    } else if(token_type() == TOK_CURLY_BR_OPEN){

        Leave* leave = new Leave(t);
        node->add_node(leave);

        next_token();

        Node* STATEMENTS_Node = STATEMENTS();
        node->add_node(STATEMENTS_Node);

        if(token_type() == TOK_CURLY_BR_CLOSE) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_BR_CLOSE"); }

        node->create(TypeChecker::checkSTATEMENT_CURLY_BR_OPEN, CodeMaker::makeSTATEMENT_CURLY_BR_OPEN);//sets the typecheck method
    } else if(token_type() == TOK_IF){
        if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

        Leave* leave = new Leave(t);
        node->add_node(leave);

        next_token();

        if(token_type() == TOK_BR_OPEN) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_BR_OPEN"); }

        Node* EXP_Node = EXP();
        node->add_node(EXP_Node);

        if(token_type() == TOK_BR_CLOSE) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_BR_CLOSE"); }

        Node* STATEMENT_Node1 = STATEMENT();
        node->add_node(STATEMENT_Node1);

        if(token_type() == TOK_ELSE) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_ELSE"); }

        Node* STATEMENT_Node2 = STATEMENT();
        node->add_node(STATEMENT_Node2);

        node->create(TypeChecker::checkSTATEMENT_IF, CodeMaker::makeSTATEMENT_IF);//sets the typecheck method
    } else if(token_type() == TOK_WHILE){
        if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

        Leave* leave = new Leave(t);
        node->add_node(leave);

        next_token();

        if(token_type() == TOK_BR_OPEN) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_BR_OPEN"); }

        Node* EXP_Node = EXP();
        node->add_node(EXP_Node);

        if(token_type() == TOK_BR_CLOSE) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_BR_CLOSE"); }

        Node* STATEMENT_Node = STATEMENT();
        node->add_node(STATEMENT_Node);

        node->create(TypeChecker::checkSTATEMENT_WHILE, CodeMaker::makeSTATEMENT_WHILE);//sets the typecheck method
    } else { error("expected STATEMENT"); }

    return node;
}

Node* Rules::EXP(){
    if(DEBUG_PRINT_TRACE_FUNCTIONS) printf("EXP\n");
    Node* node = new Node();

    Node* EXP2_Node = EXP2();
    node->add_node(EXP2_Node);

    Node* OP_EXP_Node = OP_EXP();
    node->add_node(OP_EXP_Node);

    node->create(TypeChecker::checkEXP, CodeMaker::makeEXP);//sets the typecheck method
    return node;
}

Node* Rules::EXP2(){
    if(DEBUG_PRINT_TRACE_FUNCTIONS) printf("EXP2\n");

    Node* node = new Node();

    if(token_type() == TOK_BR_OPEN){
         if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

        Leave* leave = new Leave(t);
        node->add_node(leave);

        next_token();

        Node* EXP_Node = EXP();
        node->add_node(EXP_Node);

        if(token_type() == TOK_BR_CLOSE) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_BR_CLOSE"); }

        node->create(TypeChecker::checkEXP2_BR_OPEN, CodeMaker::makeEXP2_BR_OPEN);//sets the typecheck method
    } else if(token_type() == TOK_IDENTIFIER){
        if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

        Leave* leave = new Leave(t);
        node->add_node(leave);

        next_token();
        
        Node* INDEX_Node = INDEX(); 
        node->add_node(INDEX_Node);

        node->create(TypeChecker::checkEXP2_IDENTIFIER, CodeMaker::makeEXP2_IDENTIFIER);//sets the typecheck method
    } else if(token_type() == TOK_INTEGER){
        if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

        Leave* leave = new Leave(t);
        node->add_node(leave);

        next_token();

        node->create(TypeChecker::checkEXP2_INTEGER, CodeMaker::makeEXP2_INTEGER);//sets the typecheck method
    } else if(token_type() == TOK_MINUS){
        if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

        Leave* leave = new Leave(t);
        node->add_node(leave);

        next_token();
        
        Node* EXP2_Node = EXP2();
        node->add_node(EXP2_Node);

        node->create(TypeChecker::checkEXP2_MINUS, CodeMaker::makeEXP2_MINUS);//sets the typecheck method
    } else if(token_type() == TOK_EXCL){
        if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

        Leave* leave = new Leave(t);
        node->add_node(leave);

        next_token();

        Node* EXP2_Node = EXP2();
        node->add_node(EXP2_Node);

        node->create(TypeChecker::checkEXP2_EXCL, CodeMaker::makeEXP2_EXCL);//sets the typecheck method
    } else { error("expected EXP2"); }

    return node;
}

Node* Rules::INDEX(){
    if(DEBUG_PRINT_TRACE_FUNCTIONS) printf("INDEX\n");
    Node* node = new Node();

    if(token_type() == TOK_SQ_BR_OPEN){
        if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

        Leave* leave = new Leave(t);
        node->add_node(leave);

        next_token();

        Node* EXP_Node = EXP();
        node->add_node(EXP_Node);

        if(token_type() == TOK_SQ_BR_CLOSE) {
            if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

            Leave* leave = new Leave(t);
            node->add_node(leave);

            next_token();
        } else { error("expected TOK_SQ_BR_CLOSE"); }

        node->create(TypeChecker::checkINDEX, CodeMaker::makeINDEX);//sets the typecheck method
    } else {
        //epsilon
        node->create(TypeChecker::checkINDEXe, CodeMaker::makeINDEXe);//sets the typecheck method
    }

    return node;
}

Node* Rules::OP_EXP(){
    if(DEBUG_PRINT_TRACE_FUNCTIONS) printf("OP_EXP\n");
    Node* node = new Node();

    if(token_type() == TOK_PLUS           ||
       token_type() == TOK_MINUS          ||
       token_type() == TOK_STAR           ||
       token_type() == TOK_SLASH          ||
       token_type() == TOK_ANGLE_BR_OPEN  ||
       token_type() == TOK_ANGLE_BR_CLOSE ||
       token_type() == TOK_ASSIGN         ||
       token_type() == TOK_SPECIAL        ||
       token_type() == TOK_AMPERSAND){
        Node* OP_Node = OP();
        node->add_node(OP_Node);

        Node* EXP_Node = EXP();
        node->add_node(EXP_Node);

        node->create(TypeChecker::checkOP_EXP, CodeMaker::makeOP_EXP);//sets the typecheck method
    } else {
        //epsilon
        node->create(TypeChecker::checkOP_EXPe, CodeMaker::makeOP_EXPe);//sets the typecheck method
    }

    return node;
}

Node* Rules::OP(){
    if(DEBUG_PRINT_TRACE_FUNCTIONS) printf("OP\n");
    Node* node = new Node();

    if(token_type() == TOK_PLUS           ||
       token_type() == TOK_MINUS          ||
       token_type() == TOK_STAR           ||
       token_type() == TOK_SLASH          ||
       token_type() == TOK_ANGLE_BR_OPEN  ||
       token_type() == TOK_ANGLE_BR_CLOSE ||
       token_type() == TOK_ASSIGN         ||
       token_type() == TOK_SPECIAL        ||
       token_type() == TOK_AMPERSAND){
        if(DEBUG_PRINT_TOKEN_FOUND) { printf("TOKEN FOUND:"); TokenPrinter::printToken(stdout, t, scanner); }

        Leave* leave = new Leave(t);
        node->add_node(leave);

        TType type = token_type();
        switch(type){
            case TOK_PLUS:                      node->create(TypeChecker::checkOP_PLUS, CodeMaker::makeOP_PLUS); break; //sets the typecheck method
            case TOK_MINUS:                   node->create(TypeChecker::checkOP_MINUS, CodeMaker::makeOP_MINUS); break; //sets the typecheck method
            case TOK_STAR:                   node->create(TypeChecker::checkOP_MULT, CodeMaker::makeOP_MULT); break; //sets the typecheck method
            case TOK_SLASH:                   node->create(TypeChecker::checkOP_DIV, CodeMaker::makeOP_DIV); break; //sets the typecheck method
            case TOK_ANGLE_BR_OPEN:                   node->create(TypeChecker::checkOP_LESS, CodeMaker::makeOP_LESS); break; //sets the typecheck method
            case TOK_ANGLE_BR_CLOSE:                   node->create(TypeChecker::checkOP_GREATER, CodeMaker::makeOP_GREATER); break; //sets the typecheck method
            case TOK_ASSIGN:                   node->create(TypeChecker::checkOP_EQUAL, CodeMaker::makeOP_EQUAL); break; //sets the typecheck method
            case TOK_SPECIAL:                   node->create(TypeChecker::checkOP_UNEQUAL, CodeMaker::makeOP_UNEQUAL); break; //sets the typecheck method
            case TOK_AMPERSAND:                   node->create(TypeChecker::checkOP_AND, CodeMaker::makeOP_AND); break; //sets the typecheck method
            default: error("internal error, unsupported case"); assert(false);
        }

        next_token();

    } else { error("expected OP"); }

    return  node;
}
